#include "stdafx.h"
#include "Color.h"
#include "Common.h"
#include <map>
#include <stdexcept>

namespace
{

typedef Color GetColorFunc(float);
struct ColorData
{
    ColorData(const std::string & name="", GetColorFunc * func=0) 
    : name(name), get_color_func(func)
    { }
    std::string name;
    GetColorFunc * get_color_func;
};


#define ADD_COLOR_ENTRY(name)                                        \
    color_id_to_data.insert(std::make_pair(ColorIds::name,                 \
                                           ColorData(#name, &get##name)));

typedef std::map<ColorId, ColorData> ColorIdToData;
std::map<ColorId, ColorData> color_id_to_data;
std::map<std::string, ColorId> color_name_to_id;

void _fill_color_data()
{
    if(!color_id_to_data.empty())
        return;

    ADD_COLOR_ENTRY(Black);
    ADD_COLOR_ENTRY(White);
    ADD_COLOR_ENTRY(Grey64);
    ADD_COLOR_ENTRY(Grey128);
    ADD_COLOR_ENTRY(Grey192);
    ADD_COLOR_ENTRY(Red);
    ADD_COLOR_ENTRY(Green);
    ADD_COLOR_ENTRY(DarkGreen);
    ADD_COLOR_ENTRY(LightGreen);
    ADD_COLOR_ENTRY(Blue);
    ADD_COLOR_ENTRY(DarkBlue);
    ADD_COLOR_ENTRY(LightBlue);
    ADD_COLOR_ENTRY(Orange);
    ADD_COLOR_ENTRY(Brown);
    ADD_COLOR_ENTRY(Yellow);
    ADD_COLOR_ENTRY(GreenYellow);
    ADD_COLOR_ENTRY(Pink);
    ADD_COLOR_ENTRY(Cyan);
    ADD_COLOR_ENTRY(Magenta);
    ADD_COLOR_ENTRY(Purple);        
    ADD_COLOR_ENTRY(Gold);        
    ADD_COLOR_ENTRY(Coral);        
    ADD_COLOR_ENTRY(Beige);        
    ADD_COLOR_ENTRY(PaleGoldenRod);        
    
    ColorIdToData::iterator it = color_id_to_data.begin();
    for(; it != color_id_to_data.end(); ++it)
    {
        ColorIdToData::value_type & id_and_data = *it;
        color_name_to_id.insert(std::make_pair(id_and_data.second.name, 
                                              id_and_data.first)); 
    }
}

} // anonymous namespace

ColorCallback * __color_callback;

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

Color getColorFromId(ColorId id, float opacity)
{
    _fill_color_data();

    ColorIdToData::iterator it = color_id_to_data.find(id);
    if(it != color_id_to_data.end())
    {
        return it->second.get_color_func(opacity);
    }
    throw std::invalid_argument("Unknown color id");
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

Color getColorFromName(const std::string & name, float opacity)
{
    _fill_color_data();

    std::map<std::string, ColorId>::iterator it = 
        color_name_to_id.find(name);
    if(it != color_name_to_id.end())
    {
        return Color(it->second, opacity);
    }
    throw std::invalid_argument("Unknown color id");
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

void colorFromId(ColorId id, float opacity)
{
    Color color(id, opacity);
    color();
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

void colorFromName(const std::string & name, float opacity)
{
    Color color(name, opacity);
    color();
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

Color makeBrighter(const Color & color, int times) 
{   
    Color result = color;
    for(int n = 0; n < times; ++n)
    {
        for(int i = 0; i < 3; ++i)
        {
            result[i] = result[i] < 0.8f ? result[i] * 1.25f : 1.0f;
        }
    }
    return result;
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

Color makeDarker(const Color & color, int times) 
{   
    Color result = color;
    for(int n = 0; n < times; ++n)
    {
        for(int i = 0; i < 3; ++i)
        {
            result[i] = result[i] * 0.8f;
        }
    }
    return result;
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

void setColorCallback(ColorCallback * cb) 
{   __color_callback = cb;
}  

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

Color::Color() 
{   
    set_array(rgba, 0.f, 0.f, 0.f, 1.f);
}

Color::Color(float r, float g, float b, float a) 
{   
    set_array(rgba, r, g, b, a); 
}

Color::Color(float const src[], int n) 
{   
    copy_array(rgba, src, n); 
}

Color::Color(ColorId id, float opacity) 
{   
    from_id(id, opacity);     
}

Color::Color(const std::string & name, float opacity) 
{   
    from_name(name, opacity);
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

void Color::from_id(ColorId id, float opacity) 
{   
    *this = getColorFromId(id, opacity); 
}

void Color::from_name(const std::string & name, float opacity) 
{   *this = getColorFromName(name, opacity); 
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

float const & Color::operator[](int i) const 
{   
    return rgba[i]; 
}

float & Color::operator[](int i) 
{   
    return rgba[i]; 
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

const float * Color::to_array() const 
{   
    return rgba; 
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

void Color::copy_to(float dest[4]) 
{   copy_array(dest, rgba, 4); 
}

void Color::operator()() const 
{   
    __color_callback(rgba[0], rgba[1], rgba[2], rgba[3]); 
}

void Color::operator()(float opacity) const 
{   
    __color_callback(rgba[0], rgba[1], rgba[2], opacity); 
}

// -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

Color Color::interp(Color const & from, Color const & to, float x)
{
    assert(x >= 0 && x <= 1);
    Color result;
    for(int i = 0; i < 4; ++i)
        result[i] = from[i] + (to[i] - from[i]) * x;
    return result;
}



